﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class PUEXParticle {
	
	public static uint PEF_NONE = 0;
	public static uint PEF_EMITTED = 1 << 1;
	public static uint PEF_EXPIRED = 1 << 2;
	public static uint PEF_COLLIDED = 1 << 3;

	// TTL
	public bool alive;
	public float timeToLive;
	public float totalTimeToLive;
	public float timeFraction;
	public bool freezed;

	public float setTimeFraction (float value) {
		value = Mathf.Clamp (value, 0f, 1f);
		timeToLive = totalTimeToLive - value * totalTimeToLive;
		timeFraction = value;
		return timeFraction;
	}

	// position
	public Vector3 position;
	public Vector3 originalPosition;
	public Vector3 lastPosition;

	// velocity
	public Vector3 direction;
	public Vector3 originalDirection;
	public float speed;
	public float originalSpeed;

	public Vector3 velocity () {
		return direction * speed;
	}
	public void setVelocity (Vector3 velocity) {
		direction = velocity.normalized;
		speed = velocity.magnitude;
	}
	public Vector3 originalVelocity () {
		return originalDirection * originalSpeed;
	}

	// orientation
	public Quaternion orientation;
	public Quaternion originalOrientation;
	public float rotation; // degree
	public float originalRotation;

	// rotation
	public Vector3 rotationAxis;
	public Vector3 originalRotationAxis;
	public float rotationSpeed;
	public float originalRotationSpeed;

	// size
	public float size; // z-axis dimension
	public float originalSize;
	public Vector2 scale; // x-axis dimension = size * scale.x, y-axis dimension = size * scale.y
	public Vector2 originalScale;

	public Vector3 dimensions () {
		return new Vector3 (size * scale.x, size * scale.y, size);
	}
	public void setDimensions (Vector3 value) {
		size = value.z;
		scale.x = value.x / size;
		scale.y = value.y / size;
	}
	public Vector3 originalDimensions () {
		return new Vector3 (originalSize * originalScale.x, originalSize * originalScale.y, originalSize);
	}

	public Matrix4x4 transformNoScale () {
		return Matrix4x4.TRS (position, orientation, Vector3.one);
	}
	
	public Matrix4x4 transform () {
		return Matrix4x4.TRS (position, orientation, dimensions());
	}

	// color
	public Color color;
	public Color originalColor;

	// UV
	public Vector2 uvOffset;
	public Vector2 originalUVOffset;
	public Vector2 uvScale;

	// collision
	public float mass;
	public float originalMass;

	public float boundingSphereRadius() {
		Vector3 d = dimensions ();
		//radius = 0.5 * Math::Sqrt(width*width + height*height + depth*depth);
		return 0.5f * Mathf.Max(d.z, Mathf.Max(d.x, d.y)); // approximation
	}
	
	// misc data
	public object visualData;
	public PUEXParticleSystem attachedSystem;

	// lifecycle
	public PUEXParticle() {
		alive = false;
		
		visualData = null;
		attachedSystem = null;
	}

	public void initForEmission () {
		alive = true;
		timeToLive = 10f;
		totalTimeToLive = 10f;
		timeFraction = 0f;
		freezed = false;

		position = Vector3.zero;
		originalPosition = Vector3.zero;
		lastPosition = Vector3.zero;
		
		direction = Vector3.zero;
		originalDirection = Vector3.zero;
		speed = 0f;
		originalSpeed = 0f;
		
		orientation = Quaternion.identity;
		originalOrientation = Quaternion.identity;
		rotation = 0f;
		originalRotation = 0f;

		rotationAxis = Vector3.forward;
		originalRotationAxis = Vector3.forward;
		rotationSpeed = 0f;
		originalRotationSpeed = 0f;
		
		size = 1f;
		scale = Vector2.one;
		
		color = Color.white;
		originalColor = Color.white;

		uvOffset = Vector2.zero;
		originalUVOffset = Vector2.zero;
		uvScale = Vector2.one;
		
		mass = 1f;
		originalMass = 1f;
		
		visualData = null;
		attachedSystem = null;

		eventFlags = PEF_NONE;
		addEventFlags(PEF_EMITTED);
	}

	public void process (float dt, bool orientationFollowDirection) {
		if (!freezed) {
			position += velocity () * dt;
			lastPosition = position;

			float dr = rotationSpeed * dt;
			rotation += dr;
			if (orientationFollowDirection) {
				orientation = Quaternion.LookRotation(direction, Vector3.up) * Quaternion.AngleAxis(rotation, rotationAxis);
			}
			else {
				orientation *= Quaternion.AngleAxis (dr, rotationAxis);
			}
		}

		timeToLive -= dt;
		timeFraction = (totalTimeToLive - timeToLive) / totalTimeToLive;
		if (timeToLive <= 0f) {
			kill();
		}
	}

	public void kill () {
		timeToLive = 0f;
		timeFraction = 1f;
		addEventFlags (PEF_EXPIRED);
		visualData = null;
	}

	public void freeze () {
		freezed = true;
		speed = 0f;
		rotationSpeed = 0f;
	}

	public void destroy () {
		alive = false;
		
		visualData = null;

		if (attachedSystem != null) {
			attachedSystem.forceStopParticleSystem();
			GameObject.Destroy(attachedSystem.gameObject);
		}
		attachedSystem = null;
	}

	// event flag
	public void setEventFlags(uint flags) { eventFlags = flags; }
	public void addEventFlags(uint flags) { eventFlags |= flags; }
	public void removeEventFlags(uint flags) { eventFlags &= ~flags; }
	public uint getEventFlags() { return eventFlags; }
	public bool hasEventFlags(uint flags) {return (eventFlags & flags) != 0;}
	public uint eventFlags;

}
